package com.viettel.core.memcached;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;


import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
import com.viettel.core.common.utils.StringUtility;

public class MemcachedClient {
	private static final String COLON = ":";
	private static MemcachedClient kmc = new MemcachedClient();

	public static MemcachedClient getInstance() {
		return kmc;
	}

	// create a static client as most installs only need
	// a single instance
	private MemCachedClient mcc;
	private String clientCacheId;
	private boolean isActive = false;

	private MemcachedClient() {
	}

	@SuppressWarnings("deprecation")
	public void init(String[] cacheServers, Integer[] cacheWeights,
			String clientCacheId) {
		// grab an instance of our connection pool
		SockIOPool pool = SockIOPool.getInstance();

		// set the servers and the weights
		pool.setServers(cacheServers);
		pool.setWeights(cacheWeights);

		// set some basic pool settings
		// 5 initial, 5 min, and 250 max conns
		// and set the max idle time for a conn
		// to 6 hours
		pool.setInitConn(5);
		pool.setMinConn(5);
		// pool.setMaxConn( 6 );
		pool.setMaxConn(250);
		pool.setMaxIdle(1000 * 60 * 60 * 6);

		// set the sleep for the maint thread
		// it will wake up every x seconds and
		// maintain the pool size
		pool.setMaintSleep(30);

		// set some TCP settings
		// disable nagle
		// set the read timeout to 3 secs
		// and don't set a connect timeout
		pool.setNagle(false);
		pool.setSocketTO(3000);
		pool.setSocketConnectTO(0);

		// initialize the connection pool
		pool.initialize();

		// lets set some compression on for the client
		// compress anything larger than 64k
		mcc = new MemCachedClient();
		mcc.setCompressEnable(true);
		mcc.setCompressThreshold(64 * 1024);

		this.clientCacheId = clientCacheId;
		FinderPath.initClientCacheId(clientCacheId);

		isActive = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * vn.kunkun.cache.memcached.KunkunCache#getEntity(java.util.Collection)
	 */
	public Collection<Object> getEntity(Collection<String> keys) {
		MemCachedClient memcachedClient = null;
		if (!isActive()) {
			return null;
		}
		try {
			memcachedClient = mcc;
			List<String> processedKeys = new ArrayList<String>(keys.size());

			for (String key : keys) {
				String processedKey = processKey(key);

				processedKeys.add(processedKey);
			}
			Map<String, Object> values = null;
			values = memcachedClient.getMulti((String[]) processedKeys
					.toArray());
			return values.values();
		} catch (Exception e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#getEntity(java.lang.Class,
	 * java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public <T> T getEntity(Class<T> clazz, String primaryKey) {
		if (!isActive()) {
			return null;
		}
		try {
			String processedKey = processKey(clazz.getName() + COLON
					+ primaryKey);
			return (T) mcc.get(processedKey);
		} catch (Exception e) {
			return null;
		}

	}

	public Object getObject(String key) {
		if (!isActive()) {
			return null;
		}
		try {
			String processedKey = processKey(key);
			Object res = mcc.get(processedKey);

			return res;
		} catch (Exception e) {
			return null;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#replaceEntity(java.lang.Class,
	 * java.lang.String, java.lang.Object)
	 */
	public <T> void replaceEntity(String primaryKey, T obj) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(obj.getClass().getName() + COLON
					+ primaryKey);
			if (mcc.get(processedKey) != null) {
				mcc.replace(processedKey, obj);
			} else {
				mcc.set(processedKey, obj);
			}
		} catch (Exception e) {
			return;
		}

	}

	public void replaceObject(String key, Object obj) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(key);
			if (mcc.get(processedKey) != null) {
				mcc.replace(processedKey, obj);
			} else {
				mcc.set(processedKey, obj);
			}
		} catch (Exception e) {
			return;
		}

	}
	
	public void putObject(String key, Object obj, int ttl) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(key);
			if (ttl > 0) {
				mcc.set(processedKey, obj, ttl);
			} else {
				mcc.set(processedKey, obj);
			}

		} catch (Exception e) {
			return;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#putEntity(java.lang.Class,
	 * java.lang.String, java.lang.Object)
	 */
	public <T> void putEntity(String id, T obj) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(obj.getClass().getName() + COLON
					+ id);
			mcc.set(processedKey, obj);
		} catch (Exception e) {
			return;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#putEntity(java.lang.Class,
	 * java.lang.String, java.lang.Object, int)
	 */
	public <T> void putEntity(String key, T obj, int timeToLive) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(obj.getClass().getName() + COLON
					+ key);
			mcc.set(processedKey, obj, timeToLive);
		} catch (Exception e) {
			return;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#removeEntity(java.lang.Class,
	 * java.lang.String)
	 */
	public void removeEntity(Class<?> classObj, String key) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(classObj.getName() + COLON + key);
			mcc.delete(processedKey);
		} catch (Exception e) {
			return;
		}

	}

	/**
	 * Removes the object.
	 * 
	 * @param key
	 *            the key
	 */
	public void removeObject(String key) {
		if (!isActive()) {
			return;
		}
		try {
			String processedKey = processKey(key);
			mcc.delete(processedKey);
		} catch (Exception e) {
			return;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see vn.kunkun.cache.memcached.KunkunCache#removeAll()
	 */
	public void removeAll() {
		if (!isActive()) {
			return;
		}
		try {
			mcc.flushAll();
		} catch (Exception e) {
			return;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * vn.kunkun.cache.memcached.KunkunCache#getSQLResult(vn.kunkun.cache.memcached
	 * .FinderPath, java.lang.Object[])
	 */
	public Object getSQLResult(FinderPath finderPath, Object[] args) {
		if (!isActive()) {
			return null;
		}
		try {
			String localCacheKey = null;

			localCacheKey = finderPath.encodeLocalCacheKey(args);

			Object res = mcc.get(localCacheKey);

			return res;
		} catch (Exception e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * vn.kunkun.cache.memcached.KunkunCache#putSQLResult(vn.kunkun.cache.memcached
	 * .FinderPath, java.lang.Object[], java.lang.Object)
	 */
	public void putSQLResult(FinderPath finderPath, Object[] args, Object result) {
		if (!isActive()) {
			return;
		}
		@SuppressWarnings("unchecked")
		List<String> listCache = (List<String>) mcc.get(processKey(finderPath
				.getClassName()));
		if (listCache == null) {
			listCache = new ArrayList<String>();
		}
		String localCacheKey = finderPath.encodeLocalCacheKey(args);
		listCache.add(localCacheKey);

		mcc.set(processKey(finderPath.getClassName()), listCache);
		mcc.set(localCacheKey, result);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * vn.kunkun.cache.memcached.KunkunCache#clearSQLCache(java.lang.String)
	 */
	public void clearSQLCache(String groupCache) {
		if (!isActive()) {
			return;
		}
		@SuppressWarnings("unchecked")
		List<String> listCache = (List<String>) mcc.get(processKey(groupCache));
		if (listCache != null) {
			for (String key : listCache) {
				mcc.delete(key);
			}
		}
	}
	
	public void flushAll() {
		if (!isActive()) {
			return;
		}
		mcc.flushAll();
	}

	/**
	 * Process key.
	 * 
	 * @param key
	 *            the key
	 * @return the string
	 */
	private String processKey(String key) {
		key = clientCacheId.concat(key);
		return StringUtility.getMD5Hash(key, "");
	}

	private boolean isActive() {
		return isActive;
	}
}
